<template>
  <!-- 主容器 -->
  <div class="chat-wrapper">
    <!-- 侧边栏历史记录按钮 -->
    <div class="history-button-container">
      <van-icon name="wap-nav" size="20" @click="showSidebar = true" class="history-button" />
    </div>

    <ChatSidebar v-model="showSidebar" :history-list="chatHistory" @new-chat="handleNewChat"
      @select-chat="handleSelectChat" @edit-chat="handleEditChat" @delete-chat="handleDeleteChat" />

    <!-- 聊天内容容器 -->
    <div class="chat-container">
      <!-- 对话内容区域 -->
      <div class="chat-content" ref="chatContent">
        <div v-for="(msg, index) in messages" :key="index" class="message" :class="msg.type">
          <div class="message-bubble">
            <div class="message-header">
              <van-icon v-if="msg.type === 'user'" name="user-circle-o" />
              <van-image v-else round width="2.5rem" height="2.5rem" class="avatar" :src="aiAvatar" />
              <span>{{ msg.type === 'user' ? '您' : 'AI律师' }}</span>
            </div>
            <!-- 直接使用渲染后的HTML -->
            <div class="message-text" v-html="msg.renderedText || msg.text"></div>
            <div class="message-time">{{ msg.time }}</div>
          </div>
        </div>

        <!-- 流式响应时的加载指示器 -->
        <div v-if="isStreaming" class="message ai">
          <div class="message-bubble">
            <div class="message-header">
              <van-image round width="2.5rem" height="2.5rem" class="avatar" :src="aiAvatar" />
              <span>AI律师</span>
            </div>
            <div class="message-text">
              <!-- 流式内容直接显示渲染后的HTML -->
              <span v-html="streamingRenderedContent"></span>
              <span class="cursor">|</span>
            </div>
          </div>
        </div>
      </div>

      <!-- 底部输入区域 -->
      <div class="input-area">
        <!-- 更多功能面板 -->
        <van-popup :show="showMorePanel" position="bottom" round :style="morePanelStyle" close-on-click-overlay
          @click-overlay="showMorePanel = false">
          <div class="more-panel">
            <van-grid :column-num="4" :border="false">
              <van-grid-item icon="photo-o" text="图片" @click="handleUpload('image')" />
              <van-grid-item icon="description-o" text="文件" @click="handleUpload('file')" />
              <van-grid-item icon="video-o" text="视频" @click="handleUpload('video')" />
              <van-grid-item icon="service-o" text="音频" @click="handleUpload('audio')" />
              <!-- 移除录音选项 -->
            </van-grid>
          </div>
        </van-popup>

        <!-- 文件上传对话框 -->
        <van-dialog v-model:show="showUploadDialog" :title="uploadTitle" show-cancel-button @confirm="handleFileConfirm"
          class="upload-dialog">
          <div class="uploader-container">
            <van-uploader v-model="fileList" :after-read="afterRead" :max-count="1" :accept="uploadAccept"
              :preview-image="false">
              <!-- 自定义上传区域 -->
              <div class="custom-uploader">
                <van-icon :name="uploadIcon" size="48" :color="iconColor" />
                <div class="upload-text">{{ uploadHint }}</div>
              </div>
            </van-uploader>

            <!-- 显示已选文件信息 -->
            <div v-if="fileList.length > 0" class="file-info">
              <van-icon name="description" size="20" />
              <span class="file-name">{{ fileList[0].file.name }}</span>
              <span class="file-size">({{ formatFileSize(fileList[0].file.size) }})</span>
            </div>
          </div>
        </van-dialog>

        <div class="input-box">
          <van-button round size="small" class="more-btn" @click="toggleMorePanel">
            <van-icon name="add-o" size="20" />
          </van-button>

          <div class="input-field-container" :class="{ 'has-scroll': shouldScroll }">
            <van-field v-model="userInput" rows="1" type="textarea" placeholder="请输入法律问题..." class="input-field"
              @keyup.enter="sendMessage" @input="checkScroll" ref="inputField" />
          </div>

          <van-button size="small" type="primary" class="send-btn" @click="sendMessage"
            :disabled="!userInput.trim() || isStreaming">
            <van-icon name="guide-o" size="18" />
          </van-button>

          <van-button round size="small" class="voice-btn" @touchstart="startVoiceInput" @touchend="stopVoiceInput"
            @mousedown="startVoiceInput" @mouseup="stopVoiceInput" :disabled="isStreaming">
            <van-icon :name="isRecording ? 'pause-circle-o' : 'volume-o'" size="20" />
          </van-button>
        </div>
      </div>

      <!-- 语音输入通知 -->
      <van-notify v-model:show="showVoiceNotify" type="primary" position="bottom" :style="voiceNotifyStyle"
        class="voice-notification">
        <div class="voice-notify">
          <van-icon name="volume-o" size="16" class="pulse-icon" />
          <span>正在语音输入中... (松开结束)</span>
        </div>
      </van-notify>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, nextTick, onMounted, computed } from 'vue';
import { showToast } from 'vant';
import axios from 'axios';
import ChatSidebar from '@/components/History.vue';
import { marked } from 'marked';
import DOMPurify from 'dompurify';
import Avatar from '@/assets/img/robot.png';

const userAvatar = 'https://img.yzcdn.cn/vant/cat.jpeg';
const aiAvatar = Avatar;

// 数据
const chatContent = ref<HTMLElement>();
const userInput = ref('');
const showMorePanel = ref(false);
const isRecording = ref(false);
const isStreaming = ref(false);
const streamingRawContent = ref('');
const streamingRenderedContent = ref('');
const streamingBuffer = ref('');
const showSidebar = ref(false);
const chatHistory = ref([
  { id: 1, title: '合同咨询', time: new Date() },
  { id: 2, title: '劳动纠纷', time: new Date() }
]);
const currentChatId = ref<string | null>(null);
const messages = ref([{
  type: 'ai',
  text: '您好，我是AI法律顾问，请问有什么法律问题可以帮您解答？',
  renderedText: '您好，我是AI法律顾问，请问有什么法律问题可以帮您解答？',
  time: formatTime(new Date())
}]);
const showVoiceNotify = ref(false);
const voiceNotifyTimer = ref<NodeJS.Timeout | null>(null);
const shouldScroll = ref(false);
const inputField = ref<HTMLElement>();



// 配置marked选项
marked.setOptions({
  breaks: true,
  gfm: true,
});

// 安全渲染Markdown
const renderMarkdown = (markdown: string) => {
  try {
    const rawHtml = marked.parse(markdown);
    return DOMPurify.sanitize(rawHtml);
  } catch (error) {
    console.error('Markdown渲染错误:', error);
    return markdown;
  }
};

// 计算属性
const morePanelStyle = computed(() => ({
  height: '25%',
  width: isDesktop.value ? '420px' : '100%',
  left: isDesktop.value ? '50%' : '0',
  transform: isDesktop.value ? 'translateX(-50%)' : 'none',
  maxWidth: '100%'
}));

const voiceNotifyStyle = computed(() => ({
  bottom: isDesktop.value ? '100px' : '180px',
  width: isDesktop.value ? '400px' : 'calc(100% - 20px)',
  left: isDesktop.value ? '50%' : '10px',
  transform: isDesktop.value ? 'translateX(-50%)' : 'none',
  margin: '0 auto',
  borderRadius: '8px'
}));

const isDesktop = computed(() => window.innerWidth >= 768);

// 方法
function formatTime(date: Date) {
  return `${date.getHours()}:${String(date.getMinutes()).padStart(2, '0')}`;
}

const generateChatId = (): string => {
  return Date.now().toString(36) + Math.random().toString(36).substring(2);
};

const handleNewChat = () => {
  currentChatId.value = generateChatId();
  const welcomeMessage = {
    type: 'ai',
    text: '您好，我是AI法律顾问，请问有什么法律问题可以帮您解答？',
    renderedText: '您好，我是AI法律顾问，请问有什么法律问题可以帮您解答？',
    time: formatTime(new Date())
  };
  messages.value = [welcomeMessage];
  chatHistory.value.unshift({
    id: currentChatId.value,
    title: '新对话',
    time: new Date()
  });
  showToast('新对话已创建');
};

const handleSelectChat = (chat: any) => {
  currentChatId.value = chat.id;
  const loadingMessage = {
    type: 'ai',
    text: `这是对话${chat.id}的历史记录，正在加载...`,
    renderedText: `这是对话${chat.id}的历史记录，正在加载...`,
    time: formatTime(new Date())
  };
  messages.value = [loadingMessage];
  showToast(`已切换到对话: ${chat.title}`);
};

const handleEditChat = (editedChat: any) => {
  const index = chatHistory.value.findIndex(c => c.id === editedChat.id);
  if (index !== -1) {
    chatHistory.value[index] = { ...editedChat };
    showToast('对话已更新');
  }
};

const handleDeleteChat = (chatId: any) => {
  chatHistory.value = chatHistory.value.filter(c => c.id !== chatId);
  if (currentChatId.value === chatId) {
    currentChatId.value = null;
    const promptMessage = {
      type: 'ai',
      text: '请选择或创建一个新对话',
      renderedText: '请选择或创建一个新对话',
      time: formatTime(new Date())
    };
    messages.value = [promptMessage];
  }
  showToast('对话已删除');
};

const checkScroll = () => {
  nextTick(() => {
    if (inputField.value) {
      const field = inputField.value.$el.querySelector('textarea');
      if (field) {
        shouldScroll.value = field.scrollHeight > 150;
        if (shouldScroll.value) {
          field.style.height = '150px';
          field.style.overflowY = 'auto';
        } else {
          field.style.height = 'auto';
          field.style.height = field.scrollHeight + 'px';
          field.style.overflowY = 'hidden';
        }
      }
    }
  });
};

const toggleMorePanel = () => {
  showMorePanel.value = !showMorePanel.value;
};

const sendMessage = async () => {
  if (!userInput.value.trim() || isStreaming.value) return;

  if (!currentChatId.value) {
    currentChatId.value = generateChatId();
    chatHistory.value.unshift({
      id: currentChatId.value,
      title: userInput.value.substring(0, 20) || '新对话',
      time: new Date()
    });
  }

  const userMessage = {
    type: 'user',
    text: userInput.value,
    renderedText: userInput.value,
    time: formatTime(new Date())
  };

  messages.value.push(userMessage);
  userInput.value = '';
  scrollToBottom();

  isStreaming.value = true;
  streamingRawContent.value = '';
  streamingRenderedContent.value = '';
  streamingBuffer.value = '';

  try {
    const response = await axios({
      method: 'post',
      url: '/ai/chat/stream',
      baseURL: 'http://localhost:8080',
      data: {
        userNo: 'US20216637',
        type: 'chat',
        chatId: currentChatId.value,
        prompt: userMessage.text
      },
      responseType: 'stream',
      onDownloadProgress: (progressEvent: any) => {
        const chunk = progressEvent.event.target.responseText;
        processStreamData(chunk);
      }
    });

    completeStreaming();
  } catch (error) {
    console.error('请求失败:', error);
    handleStreamError();
  }
};


// 重构流式数据处理
const processStreamData = (fullResponse: string) => {
  try {
    // 获取新增的内容部分, 使用slice切片，避免重复处理已处理过的内容
    const newContent = fullResponse.slice(streamingBuffer.value.length);

    if (newContent) {
      // 更新缓冲区
      streamingBuffer.value = fullResponse;

      // 累积原始内容
      streamingRawContent.value += newContent;

      // 实时渲染累积的内容
      streamingRenderedContent.value = renderMarkdown(streamingRawContent.value);

      // 滚动到底部
      scrollToBottom();
    }
  } catch (error) {
    console.error('处理流式数据时出错:', error);
  }
};

// 完成流式响应
const completeStreaming = () => {
  isStreaming.value = false;

  // 最终渲染完整内容
  const finalRenderedContent = renderMarkdown(streamingRawContent.value);

  // 添加到消息列表
  messages.value.push({
    type: 'ai',
    text: streamingRawContent.value, // 保存原始Markdown
    renderedText: finalRenderedContent, // 保存渲染后的HTML
    time: formatTime(new Date())
  });

  // 清理流式状态
  streamingRawContent.value = '';
  streamingRenderedContent.value = '';
  streamingBuffer.value = '';

  scrollToBottom();
};


// 处理流式请求错误
const handleStreamError = () => {
  isStreaming.value = false;

  const errorMessage = {
    type: 'ai',
    text: '抱歉，请求AI服务时出现错误，请稍后再试。',
    renderedText: '抱歉，请求AI服务时出现错误，请稍后再试。',
    time: formatTime(new Date())
  };

  messages.value.push(errorMessage);

  // 清理流式状态
  streamingRawContent.value = '';
  streamingRenderedContent.value = '';
  streamingBuffer.value = '';

  scrollToBottom();
};

const scrollToBottom = () => {
  nextTick(() => {
    if (chatContent.value) {
      chatContent.value.scrollTop = chatContent.value.scrollHeight;
    }
  });
};

const startVoiceInput = () => {
  if (isStreaming.value) return;
  isRecording.value = true;
  showVoiceNotify.value = true;
  // 30秒后自动停止
  voiceNotifyTimer.value = setTimeout(() => {
    stopVoiceInput();
  }, 30000);
};

const stopVoiceInput = () => {
  if (voiceNotifyTimer.value) {
    clearTimeout(voiceNotifyTimer.value);
    voiceNotifyTimer.value = null;
  }
  isRecording.value = false;
  showVoiceNotify.value = false;

  // 模拟语音识别结果
  if (Math.random() > 0.3) {
    userInput.value = "我想咨询关于劳动合同解除的法律问题";
    showToast('语音输入完成');
  } else {
    showToast('未能识别，请重试');
  }
};


/* ============== 文件上传对话框  ===================== */

// 新增的上传相关状态
const showUploadDialog = ref(false);
const fileList = ref<any[]>([]);
const uploadType = ref<'image' | 'file' | 'video' | 'audio'>('file');
// const uploadAccept = ref('*/*'); // 默认接受所有文件类型
// 根据上传类型计算 可接受的文件 accept属性
const uploadAccept = ref<string>('*/*');

// 格式化文件大小，用于显示
const formatFileSize = (bytes: number) => {
  if (bytes === 0) return '0 B';
  const k = 1024;
  const sizes = ['B', 'KB', 'MB', 'GB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
};

// 根据类型计算属性
const uploadTitle = computed(() => {
  switch (uploadType.value) {
    case 'image': return '上传图片';
    case 'video': return '上传视频';
    case 'audio': return '上传音频';
    default: return '上传文件';
  }
});

const uploadIcon = computed(() => {
  switch (uploadType.value) {
    case 'image': return 'photo-o';
    case 'video': return 'video-o';
    default: return 'description';
  }
});

const iconColor = computed(() => {
  return fileList.value.length > 0 ? '#1989fa' : '#969799';
});

const uploadHint = computed(() => {
  if (fileList.value.length > 0) return '点击更换文件';
  switch (uploadType.value) {
    case 'image': return '点击选择图片';
    case 'video': return '点击选择视频';
    case 'audio': return '点击选择音频';
    default: return '点击选择文件';
  }
});


// 处理上传按钮点击
const handleUpload = (type: 'image' | 'file' | 'video' | 'audio') => {
  uploadType.value = type;
  showMorePanel.value = false;

  // 根据类型设置accept属性
  switch (type) {
    case 'image':
      uploadAccept.value = 'image/*';
      break;
    case 'video':
      uploadAccept.value = 'video/*';
      break;
    case 'audio':
      uploadAccept.value = 'audio/*';
      break;
    default:
      uploadAccept.value = '*/*';
  }

  showUploadDialog.value = true;
  fileList.value = []; // 清空之前的选择
};

// 文件读取完成后的处理
const afterRead = (file: any) => {
  // 这里可以添加文件验证逻辑
  console.log('文件已选择:', file);
};

// 对话框确认处理
const handleFileConfirm = () => {
  if (fileList.value.length === 0) {
    showToast('请先选择文件');
    return;
  }

  const file = fileList.value[0].file;
  showToast(`已选择${uploadType.value}文件: ${file.name}`);

  // 这里可以添加文件上传逻辑
  // uploadFile(file, uploadType.value);

  // 重置状态
  fileList.value = [];
  showUploadDialog.value = false;
};

/* ============== 文件上传对话框  ===================== */


// 生命周期
onMounted(() => {
  scrollToBottom();
});
</script>

<style scoped>
/* =================================== */
/* 基础布局 */
.chat-wrapper {
  position: relative;
  height: 100%;
  width: 100%;
  display: flex;
  flex-direction: column;
}

.chat-container {
  flex: 1;
  display: flex;
  flex-direction: column;
  height: calc(100vh - var(--van-nav-bar-height) - var(--van-tabbar-height) - env(safe-area-inset-bottom));
  background-color: #f7f8fa;
  position: relative;
  overflow: hidden;
}

/* =================================== */

/* 历史按钮 */
.history-button-container {
  position: fixed;
  top: calc(var(--van-nav-bar-height) + 10px);
  left: 10px;
  z-index: 1001;
}

.history-button {
  background-color: white;
  padding: 8px;
  border-radius: 50%;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  z-index: 1001;
}

/* =================================== */
/* 聊天内容区域 */
.chat-content {
  flex: 1;
  padding: 12px;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  scroll-behavior: smooth;
}

/* =================================== */
/* 消息样式 */
.message {
  margin-bottom: 16px;
  display: flex;
}

.message.user {
  justify-content: flex-end;
}

.message.ai {
  justify-content: flex-start;
}

.message-bubble {
  max-width: 75%;
  padding: 12px 16px;
  border-radius: 18px;
  position: relative;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
  transition: all 0.2s ease;
  line-height: 1.5;
}

.message.user .message-bubble {
  background-color: #1989fa;
  color: white;
  border-bottom-right-radius: 4px;
}

.message.ai .message-bubble {
  background-color: white;
  color: #333;
  border-bottom-left-radius: 4px;
}

.message-header {
  font-size: 12px;
  margin-bottom: 4px;
  display: flex;
  align-items: center;
}

.message-header .van-icon {
  margin-right: 4px;
}

.message.user .message-header {
  color: rgba(255, 255, 255, 0.8);
}

.message.ai .message-header {
  color: #999;
}

.message-text {
  word-break: break-word;
  line-height: 1.5;
}

.message-time {
  font-size: 10px;
  text-align: right;
  margin-top: 4px;
  opacity: 0.7;
}

/* =================================== */
/* 输入区域 */
.input-area {
  padding: 12px;
  background-color: white;
  border-bottom: 1px solid #f0f0f0;
  border-radius: 16px;
  box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.08);
  position: sticky;
  bottom: 0;
  z-index: 100;
}


.input-box {
  display: flex;
  align-items: flex-end;
  gap: 8px;
}

.input-field-container {
  flex: 1;
  position: relative;
  min-height: 40px;
  max-height: 150px;
  border-radius: 18px;
  background-color: #f7f8fa;
  box-shadow:
    inset 0 1px 3px rgba(0, 0, 0, 0.05),
    0 1px 2px rgba(0, 0, 0, 0.05);
  transition: all 0.2s ease;
  display: flex;
  align-items: center;
}

.input-field-container.has-scroll {
  overflow-y: auto;
}

.input-field {
  width: 100%;
  min-height: 40px;
  max-height: 150px;
  padding: 10px 16px;
  box-sizing: border-box;
  overflow: hidden;
  border: none;
  outline: none;
  line-height: 1.5;
  font-size: 14px;
  background-color: transparent;
  resize: none;
}

.input-field-container.has-scroll .input-field {
  overflow-y: auto;
}

.input-field :deep(.van-field__control) {
  min-height: 40px;
  display: flex;
  align-items: center;
}

.input-field-container.has-scroll .input-field :deep(.van-field__control) {
  align-items: flex-start;
  min-height: auto;
}

.input-field::-webkit-scrollbar {
  width: 4px;
}

.input-field::-webkit-scrollbar-thumb {
  background-color: rgba(0, 0, 0, 0.2);
  border-radius: 2px;
}

.input-field::-webkit-scrollbar-track {
  background-color: transparent;
}

/* =================================== */
/* 按钮样式 */
.more-btn,
.voice-btn,
.send-btn {
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 12px;
  border: none;
  transition: all 0.2s ease;
  flex-shrink: 0;
  margin-bottom: 5px;
}

.more-btn,
.voice-btn {
  width: 40px;
  background-color: #f7f8fa;
  color: #1989fa;
  border-radius: 50%;
}

.send-btn {
  border-radius: 18px;
  font-weight: 500;
}

.send-btn:disabled {
  opacity: 0.5;
}

/* =================================== */

/* 更多面板 */
.more-panel {
  padding: 16px;
}

.van-uploader {
  padding: 16px;
  text-align: center;
}

.van-uploader__upload {
  width: 100%;
  height: 100px;
  margin: 0 auto;
}

/* =================================== */

/* 上传对话框样式 */
.upload-dialog {
  text-align: center;
}

.uploader-container {
  padding: 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.custom-uploader {
  width: 120px;
  height: 120px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  border: 1px dashed #ebedf0;
  border-radius: 8px;
  background-color: #f7f8fa;
  margin: 0 auto 16px;
  cursor: pointer;
  transition: all 0.3s;
}

.custom-uploader:hover {
  border-color: #1989fa;
  background-color: #f0f7ff;
}

.upload-text {
  margin-top: 8px;
  font-size: 14px;
  color: #969799;
}

.file-info {
  margin-top: 12px;
  padding: 8px 12px;
  background-color: #f7f8fa;
  border-radius: 4px;
  display: flex;
  align-items: center;
  max-width: 100%;
}

.file-name {
  margin: 0 6px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 200px;
}

.file-size {
  color: #969799;
  font-size: 12px;
}


/* =================================== */

/* 语音通知 */
.voice-notification {
  transform: translateY(0) !important;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15) !important;
}

.voice-notify {
  display: flex;
  align-items: center;
  padding: 8px 16px;
  font-size: 14px;
}

.pulse-icon {
  margin-right: 8px;
  animation: pulse 1.5s infinite;
}

/* =================================== */

/* 动画 */
@keyframes pulse {

  0%,
  100% {
    opacity: 1;
  }

  50% {
    opacity: 0.5;
  }
}

@keyframes blink {

  0%,
  100% {
    opacity: 1;
  }

  50% {
    opacity: 0;
  }
}

.cursor {
  animation: blink 1s infinite;
  opacity: 1;
}

/* =================================== */

/* 响应式设计 - 移动端 */
@media (max-width: 767px) {
  .chat-container {
    height: calc(100dvh - var(--van-nav-bar-height) - var(--van-tabbar-height) - env(safe-area-inset-bottom));
  }

  .message-bubble {
    max-width: 85%;
  }

  .input-field-container {
    max-height: 100px;
  }

  .input-field {
    max-height: 50px;
  }

  .more-btn,
  .voice-btn {
    width: 36px;
    height: 36px;
  }

  .send-btn {
    height: 36px;
  }
}

/* 响应式设计 - PC端 */
@media (min-width: 768px) {
  .chat-container {
    width: 420px;
    margin: 0 auto;
    border-radius: 12px;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
    height: calc(100vh - var(--van-nav-bar-height) - 20px);
    margin-top: 10px;
    margin-bottom: 10px;
  }

  .history-button-container {
    left: calc(50% - 200px);
  }

  .input-field-container {
    max-height: 150px;
  }

  .input-field {
    max-height: 50px;
  }
}

/* =================================== */

/* Markdown样式 */
.message-text :deep(h1),
.message-text :deep(h2),
.message-text :deep(h3) {
  margin: 1em 0 0.5em;
  line-height: 1.2;
}

.message-text :deep(p) {
  margin: 0.5em 0;
}

.message-text :deep(ul),
.message-text :deep(ol) {
  padding-left: 1.5em;
  margin: 0.5em 0;
}

.message-text :deep(li) {
  margin: 0.25em 0;
}

.message-text :deep(blockquote) {
  border-left: 3px solid #ddd;
  padding-left: 1em;
  margin: 0.5em 0;
  color: #666;
}

.message-text :deep(pre) {
  background-color: #f6f8fa;
  padding: 1em;
  border-radius: 4px;
  overflow-x: auto;
  margin: 0.5em 0;
}

.message-text :deep(code) {
  font-family: monospace;
  background-color: rgba(175, 184, 193, 0.2);
  padding: 0.2em 0.4em;
  border-radius: 4px;
  font-size: 85%;
}

.message-text :deep(a) {
  color: #1989fa;
  text-decoration: none;
}

.message-text :deep(a:hover) {
  text-decoration: underline;
}

.message-text :deep(table) {
  border-collapse: collapse;
  width: 100%;
  margin: 0.5em 0;
}

.message-text :deep(th),
.message-text :deep(td) {
  border: 1px solid #ddd;
  padding: 0.5em;
}

.message-text :deep(th) {
  background-color: #f6f8fa;
}

/* =================================== */
</style>